package com.beeweeb.services.tps.concurrent;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author guido d'albore
 */
public class UnboundedThreadPoolExecutor extends ThreadPoolExecutor  {
    Logger logger = LoggerFactory.getLogger("ThreadPoolScheduler");
    
    int threadPoolCount;

    long threadTotalProcessingTime = 0;

    long threadStartedCount = 0;
    long threadFinishedCount = 0;
    long threadAverageProcessingTime = 0;
    long threadMinimumProcessingTime = 0;
    long threadMaximumProcessingTime = 0;
    HashMap<Runnable, Long> timers = new HashMap<Runnable, Long>();

    public UnboundedThreadPoolExecutor(int threadPoolCount) {
        super(threadPoolCount, Integer.MAX_VALUE, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
        this.threadPoolCount = threadPoolCount;
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);

        threadFinishedCount++;

        Long elapsed, startTime, endTime;

        startTime   = timers.get(r);

        if(startTime != null) {
            timers.remove(r);

            endTime = System.currentTimeMillis();   // now
            elapsed = endTime - startTime;

            if((elapsed < threadMinimumProcessingTime) || (threadMinimumProcessingTime == 0)) {
                threadMinimumProcessingTime = elapsed;
            }

            if(elapsed > threadMaximumProcessingTime) {
                threadMaximumProcessingTime = elapsed;
            }

            threadTotalProcessingTime += elapsed;
            threadAverageProcessingTime = threadTotalProcessingTime / threadFinishedCount;
        }
    }

    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);

        threadStartedCount++;

        timers.put(r, System.currentTimeMillis());
    }

    public ArrayList<Runnable> getThreadsInQueue() {
        ArrayList<Runnable> result = new ArrayList<Runnable>();

        for(Object r : super.getQueue().toArray()) {
            result.add((Runnable)r);
        }

        return result;
    }
    
    public int getEnqueuedThreadsCount() {
        return super.getQueue().size();
    }

    public int getRunningThreadsCount() {
        return getActiveCount();
    }

    public long getThreadAverageProcessingTime() {
        return threadAverageProcessingTime;
    }

    public long getThreadMaximumProcessingTime() {
        return threadMaximumProcessingTime;
    }

    public long getThreadMinimumProcessingTime() {
        return threadMinimumProcessingTime;
    }

    public long getThreadStartedCount() {
        return threadStartedCount;
    }

    public long getThreadFinishedCount() {
        return threadFinishedCount;
    }
}
